/*
 * commons is a based project implemented
 * Copyright (C) 2024 Jasmine
 */
package com.commons.web.advice;

import java.lang.annotation.Annotation;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import com.commons.core.domain.pojo.ResultBody;
import com.commons.core.utils.JsonUtils;
import com.commons.web.pojo.ResponseResultBody;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 统一处理返回结果
 *
 * @author Jasmine
 * @version 1.0
 * @date 2020-10-12 9:57:57
 * @see ResponseResultBodyAdvice
 * @since JDK1.8
 */
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {
    private static Logger log = LoggerFactory.getLogger(ResponseResultBodyAdvice.class);

    private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseResultBody.class;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 判断类或者方法是否使用了 @ResponseResultBody注解
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {
        Class<?> parameterType = methodParameter.getParameterType();
        // 如果返回的信息是ResultBody包装类,就不需要再进行包装
        if (methodParameter.getParameterType().isAssignableFrom(ResultBody.class)) {
            return false;
        }
        return AnnotatedElementUtils.hasAnnotation(methodParameter.getContainingClass(), ANNOTATION_TYPE)
            || methodParameter.hasMethodAnnotation(ANNOTATION_TYPE);
    }

    /**
     * 当类或者方法使用了 @ResponseResultBody 就会调用这个方法
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType selectedContentType,
        Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
        ServerHttpResponse response) {

        // swagger 不处理
        /* String uri = request.getURI().toString();
        if(uri.contains("/v2/api-docs") || uri.contains("/swagger-resources") || uri.contains("/swagger-ui.html")) {
            return body;
        }
        */

        // SpringBoot统一响应格式带来的String双引号问题和类型强转失败问题解决
        // https://blog.csdn.net/nineya_com/article/details/114199606
        // String类型不能直接包装，要进行特殊处理，否则出现类型转换异常: class com.ResultBody cannot be cast to class java.lang.String
        if (body instanceof String) {
            return JsonUtils.writeValueAsString(ResultBody.success(body));
        }

        // 如果controller返回的是null，这里就直接返回操作成功了。如果需要返回null，这里可以设置return null。
        ResultBody responseResult = ResultBody.success(body);

        /*  Method method = methodParameter.getMethod();
        String url = request.getURI().toASCIIString();
        try {
            log.debug("Class=[{}], method=[{}], url=[{}], result=[{}]", method.getDeclaringClass().getSimpleName(),
                method.getName(), url, objectMapper.writeValueAsString(body));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }*/
        return responseResult;
    }

}